const { useState, useEffect, useRef } = React;

/* Visibility flags. A static site can't write files, so publishing state lives
   in flags.json next to the page: the site reads it, admin.html writes it (you
   upload the file). Missing or unreachable file = everything visible. */
function useFlags() {
  const [hidden, setHidden] = useState([]);
  useEffect(() => {
    let live = true;
    fetch('flags.json?t=' + Date.now())
      .then((r) => (r.ok ? r.json() : null))
      .then((j) => { if (live && j && Array.isArray(j.hidden)) setHidden(j.hidden); })
      .catch(() => {});
    return () => { live = false; };
  }, []);
  return hidden;
}

function App() {
  const [route, setRoute] = useState(() => parseRoute());
  const [menuOpen, setMenuOpen] = useState(false);

  useEffect(() => {
    /* Old bookmarked/shared #/… links still work - the hash resolves to a
       route as always, we just quietly swap the address bar for its clean
       path equivalent so it stops showing the #. */
    if (PATH_MODE && location.hash) {
      history.replaceState(null, '', routeHref(parseRoute()));
    }
    const sync = () => { setRoute(parseRoute()); setMenuOpen(false); };
    window.addEventListener('hashchange', sync);
    window.addEventListener('popstate', sync);
    window.addEventListener('sm-route', sync);
    const bare = location.pathname.replace(/\/+$/, '') === '' || /\.html?$/i.test(location.pathname);
    if (bare && (!location.hash || location.hash === '#/' || location.hash === '#')) {
      let pick = null;
      try { pick = localStorage.getItem('sm_lang'); } catch (e) { pick = null; }
      if (pick !== 'en' && pick !== 'cs') {
        const tags = (navigator.languages && navigator.languages.length ? navigator.languages : [navigator.language || 'en']);
        pick = tags.some((l) => /^(cs|sk)\b/i.test(String(l))) ? 'cs' : 'en';
      }
      if (PATH_MODE) {
        history.replaceState(null, '', pick === 'cs' ? '/cs/' : '/');
      } else {
        location.replace(pick === 'cs' ? '#/cs/' : '#/');
      }
      setRoute(parseRoute());
    }
    return () => {
      window.removeEventListener('hashchange', sync);
      window.removeEventListener('popstate', sync);
      window.removeEventListener('sm-route', sync);
    };
  }, []);

  useEffect(() => {
    document.title = docTitle(route);
    document.documentElement.lang = SITE[route.lang].htmlLang;
    /* Every route already has its own URL and title; the description follows
       so a static build can lift <head> per page as-is. Articles describe
       themselves, everything else uses the positioning line. */
    const s2 = SITE[route.lang];
    const pool = route.screen === 'cases' ? 'cases' : 'posts';
    const art = route.slug ? (s2[pool] || []).find((p) => p.slug === route.slug) : null;
    const pm = (s2.meta || {})[route.screen];
    const desc = (art && (art.metaDesc || art.standfirst || art.excerpt || art.deck)) || (pm && pm.desc) || s2.positioning || '';
    /* Head sync per route: description, canonical, OG and the EN/CS alternates.
       The static build lifts these as rendered, one page at a time; the domain
       matches the production path contract in router.js. */
    const upsert = (sel, make, content) => {
      let el = document.querySelector(sel);
      if (content === null || content === '') { if (el) el.remove(); return null; }
      if (!el) { el = make(); document.head.appendChild(el); }
      if (content !== undefined) el.setAttribute(el.tagName === 'LINK' ? 'href' : 'content', content);
      return el;
    };
    const mk = (tag, attrs) => () => { const e = document.createElement(tag); for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, v); return e; };
    const base = 'https://sevcikmarketing.com';
    /* routeHref() already returns a leading-slash path in PATH_MODE ('/cs/sluzby/');
       in hash mode it's '#/cs/sluzby/', so only that form needs the '#' stripped. */
    const pathOf = (r) => (PATH_MODE ? routeHref(r) : (routeHref(r).slice(1) || '/'));
    const path = pathOf(route);
    upsert('meta[name="description"]', mk('meta', { name: 'description' }), desc);
    /* Legal pages exist for people, not for search results. */
    upsert('meta[name="robots"]', mk('meta', { name: 'robots' }), (['privacy', 'cookies', 'gtc'].includes(route.screen) || (art && art.soon)) ? 'noindex,follow' : 'index,follow');
    upsert('link[rel="canonical"]', mk('link', { rel: 'canonical' }), base + path);
    upsert('meta[property="og:title"]', mk('meta', { property: 'og:title' }), document.title);
    upsert('meta[property="og:description"]', mk('meta', { property: 'og:description' }), desc);
    upsert('meta[property="og:url"]', mk('meta', { property: 'og:url' }), base + path);
    const ogType = document.querySelector('meta[property="og:type"]');
    if (ogType) ogType.setAttribute('content', art ? 'article' : 'website');
    const ogCard = (() => {
      const k = ['privacy', 'cookies', 'gtc'].includes(route.screen) ? 'legal' : route.screen;
      return ['home', 'about', 'services', 'contact', 'blog', 'cases', 'legal'].includes(k)
        ? '/assets/og/' + route.lang + '-' + k + '.png'
        : null;
    })();
    const ogImg = base + (ogCard || '/assets/photos/martin-cta.png');
    upsert('meta[property="og:image"]', mk('meta', { property: 'og:image' }), ogImg);
    upsert('meta[property="og:image:width"]', mk('meta', { property: 'og:image:width' }), ogCard ? '1200' : '');
    upsert('meta[property="og:image:height"]', mk('meta', { property: 'og:image:height' }), ogCard ? '630' : '');
    upsert('meta[property="og:image:alt"]', mk('meta', { property: 'og:image:alt' }), s2.siteName + ' - ' + (s2.positioning || ''));
    upsert('meta[property="og:locale"]', mk('meta', { property: 'og:locale' }), route.lang === 'cs' ? 'cs_CZ' : 'en_GB');
    upsert('meta[property="og:locale:alternate"]', mk('meta', { property: 'og:locale:alternate' }), route.lang === 'cs' ? 'en_GB' : 'cs_CZ');
    upsert('meta[name="twitter:title"]', mk('meta', { name: 'twitter:title' }), document.title);
    upsert('meta[name="twitter:description"]', mk('meta', { name: 'twitter:description' }), desc);
    upsert('meta[name="twitter:image"]', mk('meta', { name: 'twitter:image' }), ogImg);
    /* Article-only properties; cleared elsewhere so a share of the home page
       never inherits the last post's date. */
    const artTime = art && art.iso;
    upsert('meta[property="article:published_time"]', mk('meta', { property: 'article:published_time' }), artTime || '');
    upsert('meta[property="article:author"]', mk('meta', { property: 'article:author' }), art ? s2.siteName : '');
    upsert('link[rel="alternate"][hreflang="en"]', mk('link', { rel: 'alternate', hreflang: 'en' }), base + pathOf(swapLang(route, 'en')));
    upsert('link[rel="alternate"][hreflang="cs"]', mk('link', { rel: 'alternate', hreflang: 'cs' }), base + pathOf(swapLang(route, 'cs')));
    /* x-default points at the primary language for visitors whose locale
       matches neither tree. */
    upsert('link[rel="alternate"][hreflang="x-default"]', mk('link', { rel: 'alternate', hreflang: 'x-default' }), base + pathOf(swapLang(route, 'en')));
  }, [route]);

  /* A layer covers the viewport, so the page behind it must not scroll. */
  useEffect(() => {
    const layered = ['about', 'contact', 'blog', 'cases', 'services', 'privacy', 'cookies', 'gtc', 'newsletterConfirm'].includes(route.screen);
    document.documentElement.style.overflow = layered ? 'hidden' : '';
    return () => { document.documentElement.style.overflow = ''; };
  }, [route.screen]);

  const { lang, screen, slug } = route;
  const hidden = useFlags();
  const s = React.useMemo(() => {
    const base = SITE[lang];
    if (!hidden.length) return base;
    return { ...base, posts: base.posts.filter((p) => !hidden.includes(p.id)), cases: base.cases.filter((c) => !hidden.includes(c.id)) };
  }, [lang, hidden]);
  const t = s.ui;

  const isMobile = () => window.matchMedia('(max-width: 1024px)').matches;

  const nav = (next) => {
    if (next && typeof next === 'object' && next.lang) { onLangChange(next.lang); return; }
    if (next === 'menu') { setMenuOpen((v) => !v); return; }
    goTo({ lang, screen: next, slug: null });
  };
  const openArticle = (p) => goTo({ lang, screen: 'blog', slug: p.slug });
  const openCase = (c) => goTo({ lang, screen: 'cases', slug: c.slug });
  const home = () => goTo({ lang, screen: 'home', slug: null });
  const prevScreen = useRef('home');
  const lastScreen = useRef(route.screen);
  if (lastScreen.current !== route.screen) {
    if (lastScreen.current !== 'contact') prevScreen.current = lastScreen.current;
    lastScreen.current = route.screen;
  }
  const backTarget = () => {
    const p = prevScreen.current;
    return p && p !== 'contact' ? p : 'home';
  };
  const backFromContact = () => goTo({ lang, screen: backTarget(), slug: null });
  const contactBackLabel = () => {
    const p = backTarget();
    if (p === 'home') return t.backHome;
    const item = (t.nav || []).find((n) => n.screen === p);
    return t.backTo && item ? t.backTo.replace('{name}', item.label.toLowerCase()) : t.backHome;
  };
  const onLangChange = (code) => {
    const next = LANGS[code] || 'en';
    /* Remembered, so the browser-language default never overrides a choice
       the visitor made by hand. */
    try { localStorage.setItem('sm_lang', next); } catch (e) { /* private mode */ }
    goTo(swapLang(route, next));
  };

  const article = slug ? s.posts.find((p) => p.slug === slug) : null;
  /* An unpublished study must not be reachable by deep link either - a bookmark
     or shared URL falls back to the list, which explains the state honestly. */
  const caseItem = slug ? s.cases.find((c) => c.slug === slug && !c.soon) : null;

  let content;
  if (screen === 'about') content = <About s={s} t={t} onBack={home} />;
  else if (screen === 'contact') content = <Contact s={s} t={t} onBack={backFromContact} backLabel={contactBackLabel()} />;
  else content = <Home s={s} t={t} />;

  /* Reading screens (hub, article, cases, case reader) follow the reader's
     own light/dark choice - the bar has to switch with it, not stay light. */
  const [ctaClosed, setCtaClosed] = React.useState(false);
  const readerTheme = useReaderTheme();
  const readingScreen = screen === 'blog' || screen === 'cases';
  const barDark = screen === 'services' || (readingScreen && readerTheme.dark);
  return (
    <div className="sm-shell" style={{ display: 'flex', minHeight: '100vh', background: 'var(--paper)', fontFamily: 'var(--font-body)' }}>
      <MobileBar s={s} t={t} onNavigate={nav} onMenu={() => setMenuOpen((v) => !v)} menuOpen={menuOpen} screen={screen} dark={barDark} />
      <Sidebar s={s} t={t} lang={lang} screen={screen} onNavigate={nav} onLangChange={onLangChange} menuOpen={menuOpen} onOpenArticle={openArticle} onOpenCase={openCase} />
      {content}
      <MobileCta t={t} onContact={() => nav('contact')} closed={ctaClosed} onClose={() => setCtaClosed(true)} />
      <MobileMailDot shown={ctaClosed} onContact={() => nav('contact')} label={t.contactMe} />
      {screen === 'blog' && <BlogListing s={s} t={t} onBack={home} onOpenArticle={openArticle} />}
      {screen === 'cases' && <CaseStudies s={s} t={t} onBack={home} onOpenCase={openCase} />}
      {screen === 'blog' && article && <ArticleReader s={s} t={t} post={article} onBack={() => nav('blog')} onOpenArticle={openArticle} />}
      {screen === 'cases' && caseItem && <CaseStudyReader s={s} t={t} item={caseItem} onBack={() => nav('cases')} onOpenCase={openCase} />}
      {screen === 'services' && <ServicesDetail s={s} t={t} onBack={home} onContact={() => nav('contact')} />}
      {['privacy', 'cookies', 'gtc'].includes(screen) && (
        <LegalLayer doc={s.legal[screen]} t={t} onBack={home}>
          {screen === 'cookies' ? <CookieControls l={s.legal} onDone={home} /> : null}
        </LegalLayer>
      )}
      {screen === 'newsletterConfirm' && <NewsletterConfirm t={t} onBack={home} />}
      <ConsentBanner l={s.legal} active={screen !== 'cookies'} onSettings={() => nav('cookies')} />
      {menuOpen && <NavOverlay s={s} t={t} lang={lang} onClose={() => setMenuOpen(false)} onNavigate={nav} />}
    </div>
  );
}

Object.assign(window, { App });
